Skip to content

feat: require environment_subdomain or an explicit use_legacy_domain opt-out - #226

Open
armando-rodriguez-cko wants to merge 6 commits into
mainfrom
feat/INT-1688-mandatory-subdomain
Open

feat: require environment_subdomain or an explicit use_legacy_domain opt-out#226
armando-rodriguez-cko wants to merge 6 commits into
mainfrom
feat/INT-1688-mandatory-subdomain

Conversation

@armando-rodriguez-cko

@armando-rodriguez-cko armando-rodriguez-cko commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Makes the merchant-specific subdomain (MSSD) mandatory. Callers must now call environment_subdomain(...), or explicitly opt out with the new use_legacy_domain(), which raises a DeprecationWarning from its first release. Setting both, or neither, raises CheckoutArgumentException at build time. MSSD is no longer beta and non-MSSD usage will be deprecated, so the previous silent fallback to api.checkout.com had to go.

Changes

  • checkout_sdk/checkout_sdk_builder.py — the subdomain is held as a string and the EnvironmentSubdomain is built when the configuration is assembled, so environment_subdomain() no longer has to be called after environment(); new use_legacy_domain(); new _validate_environment_settings() and _requires_environment_subdomain()
  • checkout_sdk/environment_subdomain.pycreate_url_with_subdomain raises on an invalid subdomain instead of returning the URL unchanged
  • checkout_sdk/default_sdk.py, oauth_sdk.py, previous/previous_sdk.py — validate before building; Previous/ABC exempted; the duplicated with/without-subdomain branches in all three build() methods are gone
  • tests/checkout_default_sdk_test.py — covers all four combinations plus an invalid subdomain
  • tests/checkout_configuration_test.py — the parameterised bad-subdomain case now asserts the raise instead of the silent fallback
  • tests/conftest.py + five other fixtures — every client the suite builds now chooses a domain, through configure_domain

Fixed along the way

environment_subdomain() used to build the URLs from whatever environment was set at call time, so calling it before environment() silently produced the wrong host.

The long import line in tests/payments/request_apm_payments_integration_test.py is wrapped only because pre-commit lints staged files, so touching that file surfaced a pre-existing line-too-long.

Verification

544 tests passing, 0 failing, 217 skipped. flake8 and pylint pre-commit hooks pass.

API Reference

Breaking changes

Yes, two. This needs a major release, classified and versioned when the release is cut.

  1. The merchant-specific subdomain is mandatory for the Default and DefaultOAuth platforms. Code that omitted it and relied on the implicit fallback to api.checkout.com / access.checkout.com now fails at client construction. Migration: set the subdomain, or use the legacy-domain opt-out as a temporary measure. The Previous (ABC) platform is unaffected.
  2. An invalid subdomain now fails instead of being silently ignored. Callers passing a malformed value keep working against the shared host today; after this change they fail fast. This one is easy to miss because it is not what the ticket asked for, so it needs its own line in the release notes.

README

Updated in this PR: a "Subdomain value" section above the Default example, the subdomain added to the configuration samples, and a "Legacy domain (emergency use only)" section at the bottom.

Notes

The suite routes every client it builds through a single helper that uses the shared hosts. Applying the merchant-specific subdomain there looked better, since it is the path merchants are being moved to, but the sandbox OAuth clients are not provisioned for it: .NET CI failed 224 integration tests with invalid_client when the token request went to {subdomain}.access.sandbox.checkout.com. Binding those OAuth clients to the subdomain is a platform task and should land before merchants are told the subdomain is mandatory.

Reference implementation: checkout-sdk-net#590. Tracked as INT-1688.

No version bump here: that happens on master when the release is cut, per the release workflow.

@agent-wall-e

agent-wall-e Bot commented Aug 10, 2026

Copy link
Copy Markdown

🔴 Risk Classification: MAJOR

Approval route: AI Review + Human Approval Required
Rollback controls: Change-freeze window + documented rollback plan

Classification reasons

  • security_sensitive_path:checkout_sdk/oauth_sdk.py
  • security_sensitive_path:tests/oauth_integration_test.py

Operational gates

  • ✅ jira_ticket (INT-1688)
  • ✅ independent_review

Files analysed: 15


wall-e 2026.06.19-02 · policy 376219bc71e6…

@agent-wall-e

agent-wall-e Bot commented Aug 10, 2026

Copy link
Copy Markdown
🔬 Debug — why this classification?

Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.

Reason code Kind Clause Meaning
security_sensitive_pathcheckout_sdk/oauth_sdk.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathtests/oauth_integration_test.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).

Kinds:

  • classifying — this rule contributed to the chosen tier.
  • informational — context only; did not by itself decide the tier.

See issue #3 for the proposal to formalise this map as Appendix A of the standards doc.

wall-e 2026.06.19-02 · debug

@agent-wall-e

agent-wall-e Bot commented Aug 10, 2026

Copy link
Copy Markdown

🔵 Advisory review: Sound, but needs your judgement

This PR needs a human approval. The code itself reads as correct; whether it should land depends on context I don't have.

This PR makes environment_subdomain mandatory for Default/OAuth platforms (with a use_legacy_domain() escape hatch) and removes the silent fallback to shared hosts — a correct and well-structured breaking change, but the reviewer must decide whether the team is ready to ship the blast radius of a forced migration.

For you to decide

  • The PR's own notes acknowledge that sandbox OAuth clients are NOT provisioned for the merchant-specific subdomain, meaning every integration test in the suite is forced to use use_legacy_domain() — the very deprecated path — until a separate platform task completes; this is an intentional interim state but the reviewer should confirm they're comfortable shipping a major-version SDK where the recommended path doesn't work against the published sandbox.
  • The _environment_subdomain property is now computed on every access from self._environment, which is correct since environment() must be called before build(), but if a caller sets environment_subdomain before environment() the property will silently use the default Environment.sandbox() at validation time and then again at build time — this is actually the same environment in both calls so it works, but the ordering subtlety noted in the PR description ('calling it before environment() silently produced the wrong host') is now fixed, which should be verified explicitly.
  • The _validate_environment_settings() call in PreviousSdk.build() still runs even though _requires_environment_subdomain() returns False; this is safe (the third condition short-circuits), but it means a Previous caller who accidentally sets both environment_subdomain and use_legacy_domain will get a CheckoutArgumentException — whether that's intentional or should be silently ignored for the legacy platform is a product decision.
  • There are no tests covering the PreviousSdk with neither subdomain nor legacy domain (should succeed) or with both set (should raise); given that Previous is explicitly exempted, a test asserting it does NOT raise without either setting would add safety.
  • The use_legacy_domain() warning uses stacklevel=2, which points to the direct caller of the method — correct for normal use, but when tests suppress it with warnings.catch_warnings() the stacklevel is irrelevant; this is fine.
  • The regex r'^(?:pl-)?[a-z0-9]+$' permits arbitrarily long subdomains; the README says the MSSD is exactly 8 characters, so an 8-character enforcement could be added, but the PR description doesn't promise that tightness and it's a judgement call whether to enforce length here or document only.
  • All four combinations (subdomain only, legacy only, both, neither) are tested in tests/checkout_default_sdk_test.py, and the bad-subdomain test in checkout_configuration_test.py is correctly updated to assert a raise rather than silent fallback — test coverage for the new behaviour looks adequate.

This is not an approval. wall-e cannot auto-approve this PR — it is an opinion to help whoever does. Advisory review · us.anthropic.claude-sonnet-4-6 · wall-e 2026.06.19-02

@agent-wall-e

agent-wall-e Bot commented Aug 11, 2026

Copy link
Copy Markdown

🔴 Risk Classification: MAJOR

Approval route: AI Review + Human Approval Required
Rollback controls: Change-freeze window + documented rollback plan

Classification reasons

  • security_sensitive_path:checkout_sdk/oauth_sdk.py
  • security_sensitive_path:tests/oauth_integration_test.py

Operational gates

  • ✅ jira_ticket (INT-1688)
  • ✅ independent_review

Files analysed: 15


wall-e 2026.06.19-02 · policy 376219bc71e6…

@agent-wall-e

agent-wall-e Bot commented Aug 11, 2026

Copy link
Copy Markdown
🔬 Debug — why this classification?

Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.

Reason code Kind Clause Meaning
security_sensitive_pathcheckout_sdk/oauth_sdk.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathtests/oauth_integration_test.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).

Kinds:

  • classifying — this rule contributed to the chosen tier.
  • informational — context only; did not by itself decide the tier.

See issue #3 for the proposal to formalise this map as Appendix A of the standards doc.

wall-e 2026.06.19-02 · debug

@agent-wall-e

agent-wall-e Bot commented Aug 11, 2026

Copy link
Copy Markdown

🔴 Risk Classification: MAJOR

Approval route: AI Review + Human Approval Required
Rollback controls: Change-freeze window + documented rollback plan

Classification reasons

  • security_sensitive_path:.github/workflows/build-main.yml
  • security_sensitive_path:.github/workflows/build-pull-request.yml
  • security_sensitive_path:.github/workflows/build-release.yml
  • security_sensitive_path:checkout_sdk/oauth_sdk.py
  • security_sensitive_path:tests/oauth_integration_test.py

Operational gates

  • ✅ jira_ticket (INT-1688)
  • ✅ independent_review

Files analysed: 18


wall-e 2026.06.19-02 · policy 376219bc71e6…

@agent-wall-e

agent-wall-e Bot commented Aug 11, 2026

Copy link
Copy Markdown
🔬 Debug — why this classification?

Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.

Reason code Kind Clause Meaning
security_sensitive_path.github/workflows/build-main.yml classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_path.github/workflows/build-pull-request.yml classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_path.github/workflows/build-release.yml classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathcheckout_sdk/oauth_sdk.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathtests/oauth_integration_test.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).

Kinds:

  • classifying — this rule contributed to the chosen tier.
  • informational — context only; did not by itself decide the tier.

See issue #3 for the proposal to formalise this map as Appendix A of the standards doc.

wall-e 2026.06.19-02 · debug

@agent-wall-e

agent-wall-e Bot commented Aug 12, 2026

Copy link
Copy Markdown

🔴 Risk Classification: MAJOR

Approval route: AI Review + Human Approval Required
Rollback controls: Change-freeze window + documented rollback plan

Classification reasons

  • security_sensitive_path:.github/workflows/build-main.yml
  • security_sensitive_path:.github/workflows/build-pull-request.yml
  • security_sensitive_path:.github/workflows/build-release.yml
  • security_sensitive_path:checkout_sdk/oauth_sdk.py
  • security_sensitive_path:tests/oauth_integration_test.py

Operational gates

  • ✅ jira_ticket (INT-1688)
  • ✅ independent_review

Files analysed: 17


wall-e 2026.06.19-02 · policy 376219bc71e6…

@agent-wall-e

agent-wall-e Bot commented Aug 12, 2026

Copy link
Copy Markdown
🔬 Debug — why this classification?

Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.

Reason code Kind Clause Meaning
security_sensitive_path.github/workflows/build-main.yml classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_path.github/workflows/build-pull-request.yml classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_path.github/workflows/build-release.yml classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathcheckout_sdk/oauth_sdk.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathtests/oauth_integration_test.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).

Kinds:

  • classifying — this rule contributed to the chosen tier.
  • informational — context only; did not by itself decide the tier.

See issue #3 for the proposal to formalise this map as Appendix A of the standards doc.

wall-e 2026.06.19-02 · debug

@agent-wall-e

agent-wall-e Bot commented Aug 12, 2026

Copy link
Copy Markdown

🔴 Risk Classification: MAJOR

Approval route: AI Review + Human Approval Required
Rollback controls: Change-freeze window + documented rollback plan

Classification reasons

  • security_sensitive_path:checkout_sdk/oauth_sdk.py
  • security_sensitive_path:tests/oauth_integration_test.py

Operational gates

  • ✅ jira_ticket (INT-1688)
  • ✅ independent_review

Files analysed: 14


wall-e 2026.06.19-02 · policy 376219bc71e6…

@agent-wall-e

agent-wall-e Bot commented Aug 12, 2026

Copy link
Copy Markdown
🔬 Debug — why this classification?

Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.

Reason code Kind Clause Meaning
security_sensitive_pathcheckout_sdk/oauth_sdk.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathtests/oauth_integration_test.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).

Kinds:

  • classifying — this rule contributed to the chosen tier.
  • informational — context only; did not by itself decide the tier.

See issue #3 for the proposal to formalise this map as Appendix A of the standards doc.

wall-e 2026.06.19-02 · debug

…opt-out

The merchant-specific subdomain is how merchants should reach the API, but it
was optional and an unset value silently fell back to api.checkout.com, so a
forgotten subdomain looked exactly like a deliberate opt-out and the SDK could
not warn about either. Callers must now choose: set environment_subdomain, or
call use_legacy_domain(), which raises a DeprecationWarning from its first
release. Both, or neither, raises CheckoutArgumentException.

An invalid subdomain now raises instead of being quietly ignored, which is a
second breaking change: callers passing a malformed value are currently served
by the shared host and never find out.

environment_subdomain no longer needs environment() to be set first, since the
EnvironmentSubdomain is now built when the configuration is assembled. That also
removed the duplicated with/without-subdomain branches in all three build()
methods.

The Previous (ABC) platform predates merchant-specific subdomains and stays
exempt via _requires_environment_subdomain().

Fixtures route clients through conftest.configure_domain, which uses the shared
hosts: the sandbox OAuth clients are not provisioned for the subdomain, so
applying it makes every client_credentials request return invalid_client.

The long import line in the APM test is wrapped only because pre-commit lints
staged files, so touching that file surfaced a pre-existing violation.

Mirrors checkout-sdk-net#590. Refs INT-1688.
Flagged in review: the test called use_legacy_domain() directly, so it emitted
the deprecation warning on every run, inconsistent with every other fixture.
It now goes through conftest.configure_domain like the rest.
The suite could only run against the shared hosts, so the subdomain path this PR
makes mandatory had no integration coverage. Reviewers flagged that on every SDK,
and it is the right thing to flag.

The domain helper now has two modes. Default is unchanged, the shared hosts,
because the sandbox OAuth clients are not provisioned for the subdomain and the
token request returns invalid_client. Set CHECKOUT_TEST_USE_SUBDOMAIN=true and the
suite runs against CHECKOUT_MERCHANT_SUBDOMAIN instead, so once sandbox is
provisioned like production it is a one-line change in the workflows, already
wired and documented, rather than a rewrite of every fixture.

The switch is deliberately separate from CHECKOUT_MERCHANT_SUBDOMAIN, which CI
already exports: provisioning should drive the behaviour, not the presence of a
secret.
Versions are bumped on master during the release, not in a feature branch, per
the release workflow. This branch should carry only the change itself; the major
bump is classified and applied when the release is cut.
Two problems with the previous approach. It needed a new variable in 21 workflow
files, which is not viable without access to create secrets. And it wrapped the
builder chain in a configureDomain helper that is not part of the public API, so
the tests stopped looking like the code a merchant would actually write.

Every fixture now calls the real opt-out inline, in the chain, with a comment
saying why: the sandbox OAuth clients are not provisioned for the merchant-specific
subdomain, so the token request comes back invalid_client. When sandbox is
provisioned, those calls become the subdomain setter.

The unit tests covering all four combinations are untouched: they already used the
public API directly.
@armando-rodriguez-cko
armando-rodriguez-cko force-pushed the feat/INT-1688-mandatory-subdomain branch from 193295e to 042c2d1 Compare August 28, 2026 06:25
@agent-wall-e

agent-wall-e Bot commented Aug 28, 2026

Copy link
Copy Markdown

🔴 Risk Classification: MAJOR

Approval route: AI Review + Human Approval Required
Rollback controls: Change-freeze window + documented rollback plan

Classification reasons

  • security_sensitive_path:checkout_sdk/oauth_sdk.py
  • security_sensitive_path:tests/oauth_integration_test.py

Operational gates

  • ✅ jira_ticket (INT-1688)
  • ✅ independent_review

Files analysed: 15


wall-e 2026.06.19-02 · policy 376219bc71e6…

@agent-wall-e

agent-wall-e Bot commented Aug 28, 2026

Copy link
Copy Markdown
🔬 Debug — why this classification?

Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.

Reason code Kind Clause Meaning
security_sensitive_pathcheckout_sdk/oauth_sdk.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathtests/oauth_integration_test.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).

Kinds:

  • classifying — this rule contributed to the chosen tier.
  • informational — context only; did not by itself decide the tier.

See issue #3 for the proposal to formalise this map as Appendix A of the standards doc.

wall-e 2026.06.19-02 · debug

Comment thread checkout_sdk/properties.py Outdated
@@ -1 +1 @@
VERSION = "3.13.0"
VERSION = "3.12.0"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What?, should not this be 3.14.0?

@agent-wall-e

agent-wall-e Bot commented Aug 28, 2026

Copy link
Copy Markdown

🔴 Risk Classification: MAJOR

Approval route: AI Review + Human Approval Required
Rollback controls: Change-freeze window + documented rollback plan

Classification reasons

  • security_sensitive_path:checkout_sdk/oauth_sdk.py
  • security_sensitive_path:tests/oauth_integration_test.py

Operational gates

  • ✅ jira_ticket (INT-1688)
  • ✅ independent_review

Files analysed: 14


wall-e 2026.06.19-02 · policy 376219bc71e6…

@agent-wall-e

agent-wall-e Bot commented Aug 28, 2026

Copy link
Copy Markdown
🔬 Debug — why this classification?

Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.

Reason code Kind Clause Meaning
security_sensitive_pathcheckout_sdk/oauth_sdk.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).
security_sensitive_pathtests/oauth_integration_test.py classifying §2.1 M4/M5 Path matched a sensitive pattern (auth, secrets, crypto, PCI, migrations, network IaC).

Kinds:

  • classifying — this rule contributed to the chosen tier.
  • informational — context only; did not by itself decide the tier.

See issue #3 for the proposal to formalise this map as Appendix A of the standards doc.

wall-e 2026.06.19-02 · debug

@sonarqubecloud

Copy link
Copy Markdown

@armando-rodriguez-cko
armando-rodriguez-cko requested a review from a team August 28, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants